package com.nulldev.util.web.nTube.util;

import static com.nulldev.util.web.nTube.pages.Urlizer.urlizer;

import java.util.List;

import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.data.variable;
import com.nulldev.util.data.Arrays.arrays.FastArrayList;
import com.nulldev.util.data.legacy.XML.lXML;
import com.nulldev.util.data.legacy.XML.lXML.Attr;
import com.nulldev.util.data.legacy.XML.lXML.Doc;
import com.nulldev.util.data.legacy.XML.lXML.E;
import com.nulldev.util.data.longNumbers.lNumber;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.YouTubeExtractor.formats.AudioCodec;
import com.nulldev.util.web.YouTubeExtractor.formats.Format;
import com.nulldev.util.web.YouTubeExtractor.formats.VideoCodec;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata;
import com.nulldev.util.web.YouTubeExtractor.video.YouTubeVideo;

@Deprecated
public class DashLiveStreamMPDGenerator {

	private static final Logger log = LoggerFactory.getLogger();

	public static String make(final List<YouTubeVideo> videos, final YouTubeMetadata metadata, final boolean disableAV1codecs,
//			final boolean djsSingleRangeHack, final boolean invertDJSMuxingII, 
			final boolean djsRemove30FPSVideos, final variable<Boolean> has60FPS, final boolean allowWebM) {
		final Doc d = lXML.doc();
		final E mpd = d.add(lXML.e("MPD"));
		mpd.add(new Attr("xmlns", "urn:mpeg:dash:schema:mpd:2011"));
		mpd.add(new Attr("profiles", "urn:mpeg:dash:profile:isoff-live:2011"));
		mpd.add(new Attr("minBufferTime", "PT" + metadata.getExtendedMetadata().getMinBufferTime() + "S"));
		mpd.add(new Attr("type", "static"));
		mpd.add(new Attr("minimumUpdatePeriod", "PT5S"));
//		mpd.add(new Attr("mediaPresentationDuration", "PT" + (metadata.getDuration()) + "S"));
		final E period = mpd.add(lXML.e("Period"));
		period.add(new Attr("duration", "PT5S"));
		final lNumber value = lNumber.nil();
		final Executable<Void> mp4_audio = ExecutorManager.globalAsyncSubmit(() -> {
			// MPEG 4 audio
			final List<YouTubeVideo> mpeg4audio = new FastArrayList<YouTubeVideo>(true);
			if (videos != null && !videos.isEmpty()) {
				for (int i = 0; i < videos.size(); i++) {
					final YouTubeVideo e = videos.get(i);
					if (e == null) {
						log.warn("Got a null YouTubeVideo source. [index=" + i + ",e=" + e + "]");
						continue;
					}
					final Format f = e.getFormat();
					if (f == null) {
						continue;
					}
					try {
						if (f.getVideoCodec().equals(VideoCodec.NONE) && !f.getAudioCodec().equals(AudioCodec.NONE)) {
							if (f.getAudioCodec().equals(AudioCodec.AAC) && e.getCodecMime().contains("mp4a.40.2")) {
								mpeg4audio.add(e);
							}
						}
					} catch (Exception _e) {
						log.error(_e);
						continue;
					}
				}
			}
			if (!mpeg4audio.isEmpty()) {
				final int id = value.intValue();
				value.addSelf(1);
				final E adaptationSet = period.add(lXML.e("AdaptationSet"));
				adaptationSet.add(new Attr("id", String.valueOf(id)));
				adaptationSet.add(new Attr("mimeType", "audio/mp4"));
				adaptationSet.add(new Attr("startWithSAP", "1"));
				adaptationSet.add(new Attr("subsegmentAlignment", "true"));
				for (final YouTubeVideo mp4a : mpeg4audio) {
					if (mp4a == null)
						continue;
					final E representation = adaptationSet.add(lXML.e("Representation"));
					representation.add(new Attr("id", String.valueOf(mp4a.getTag())));
					representation.add(new Attr("codecs", StringsUtil.substringBetweenO(mp4a.getCodecMime(), "\"", "\"")));
					representation.add(new Attr("bandwidth", String.valueOf(mp4a.getBitrate())));
					// init (0-xxx), index (xxx-xxxx)
					final E audioChannelConfiguration = representation.add(lXML.e("AudioChannelConfiguration"));
					audioChannelConfiguration.add(new Attr("schemeIdUri", "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"));
					audioChannelConfiguration.add(new Attr("value", "2"));
					representation.add(lXML.e("BaseURL", urlizer(mp4a.videoURL())));
					if (mp4a.validSegmentPayloads()) {
						final E segmentBase = representation.add(lXML.e("SegmentBase"));
						segmentBase.add(new Attr("indexRange", /* invertDJSMuxingII ? mp4a.getInitPayload() : */ mp4a.getIndexPayload()));
						final E initialization = segmentBase.add(lXML.e("Initialization"));
//						if (djsSingleRangeHack) {
//							final String _0 = "0";
//							final String _1 = mp4a.getIndexPayload().split("-")[1];
//							initialization.add(new Attr("range", _0 + "-" + _1));
//						}else {
						initialization.add(new Attr("range", /* invertDJSMuxingII ? mp4a.getIndexPayload() : */ mp4a.getInitPayload()));
//						}
					}
				}
			}
			mpeg4audio.clear();
			log.debug("[DJS] generate::mpeg_audio");
			return null;
		});
		final Executable<Void> webm_audio = ExecutorManager.globalAsyncSubmit(() -> {
			// WebM audio (Opus/Vorbis)
			final List<YouTubeVideo> webmAudio = new FastArrayList<YouTubeVideo>(true);
			if (videos != null && !videos.isEmpty()) {
				for (int i = 0; i < videos.size(); i++) {
					final YouTubeVideo e = videos.get(i);
					if (e == null) {
						log.warn("Got a null YouTubeVideo source. [index=" + i + ",e=" + e + "]");
						continue;
					}
					final Format f = e.getFormat();
					if (f == null) {
						continue;
					}
					if (f.getVideoCodec().equals(VideoCodec.NONE) && !f.getAudioCodec().equals(AudioCodec.NONE)) {
						if (f.getAudioCodec().equals(AudioCodec.OPUS) && e.getCodecMime().contains("opus")) {
							webmAudio.add(e);
						}
					}
				}
			}
			if (!webmAudio.isEmpty()) {
				final int id = value.intValue();
				value.addSelf(1);
				final E adaptationSet = period.add(lXML.e("AdaptationSet"));
				adaptationSet.add(new Attr("id", String.valueOf(id)));
				adaptationSet.add(new Attr("mimeType", "audio/webm"));
				adaptationSet.add(new Attr("startWithSAP", "1"));
				adaptationSet.add(new Attr("subsegmentAlignment", "true"));
				for (final YouTubeVideo webm : webmAudio) {
					if (webm == null)
						continue;
					final E representation = adaptationSet.add(lXML.e("Representation"));
					representation.add(new Attr("id", String.valueOf(webm.getTag())));
					representation.add(new Attr("codecs", StringsUtil.substringBetweenO(webm.getCodecMime(), "\"", "\"")));
					representation.add(new Attr("bandwidth", String.valueOf(webm.getBitrate())));
					// init (0-xxx), index (xxx-xxxx)
					final E audioChannelConfiguration = representation.add(lXML.e("AudioChannelConfiguration"));
					audioChannelConfiguration.add(new Attr("schemeIdUri", "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"));
					audioChannelConfiguration.add(new Attr("value", "2"));
					representation.add(lXML.e("BaseURL", urlizer(webm.videoURL())));
					if (webm.validSegmentPayloads()) {
						final E segmentBase = representation.add(lXML.e("SegmentBase"));
						segmentBase.add(new Attr("indexRange", /* invertDJSMuxingII ? webm.getInitPayload() : */ webm.getIndexPayload()));
						final E initialization = segmentBase.add(lXML.e("Initialization"));
//						if (djsSingleRangeHack) {
//							final String _0 = "0";
//							final String _1 = webm.getIndexPayload().split("-")[1];
//							initialization.add(new Attr("range", _0 + "-" + _1));
//						}else {
						initialization.add(new Attr("range", /* invertDJSMuxingII ? webm.getIndexPayload() : */ webm.getInitPayload()));
//						}
					}
				}
			}
			webmAudio.clear();
			log.debug("[DJS] generate::webm_audio");
			return null;
		});
		final Executable<Void> mp4_video_all = ExecutorManager.globalAsyncSubmit(() -> {
			// MP4 Video (-AV1)
			final List<YouTubeVideo> mp4video = new FastArrayList<YouTubeVideo>(true);
			if (videos != null && !videos.isEmpty()) {
				for (int i = 0; i < videos.size(); i++) {
					final YouTubeVideo e = videos.get(i);
					if (e == null) {
						log.warn("Got a null YouTubeVideo source. [index=" + i + ",e=" + e + "]");
						continue;
					}
					final Format f = e.getFormat();
					if (f == null) {
						continue;
					}
					if (f.getVideoCodec().equals(VideoCodec.H264) && f.getAudioCodec().equals(AudioCodec.NONE)) {
						mp4video.add(e);
					}
				}
			}
			if (!mp4video.isEmpty()) {
				final int id = value.intValue();
				value.addSelf(1);
				final E adaptationSet = period.add(lXML.e("AdaptationSet"));
				adaptationSet.add(new Attr("id", String.valueOf(id)));
				adaptationSet.add(new Attr("mimeType", "video/mp4"));
				adaptationSet.add(new Attr("startWithSAP", "1"));
				adaptationSet.add(new Attr("subsegmentAlignment", "true"));
				adaptationSet.add(new Attr("scanType", "progressive"));
				for (final YouTubeVideo mp4 : mp4video) {
					if (mp4 == null)
						continue;
					if ((djsRemove30FPSVideos && has60FPS.value() && (mp4.getFPS() == 24 || mp4.getFPS() == 30))
							&& (mp4.getFormat().getHeight() == 720 || mp4.getFormat().getHeight() == 1080 || mp4.getFormat().getHeight() == 1440
									|| mp4.getFormat().getHeight() == 2160 || mp4.getFormat().getHeight() == 4320)) {
						continue;
					}
					final E representation = adaptationSet.add(lXML.e("Representation"));
					representation.add(new Attr("id", String.valueOf(mp4.getTag())));
					representation.add(new Attr("codecs", StringsUtil.substringBetweenO(mp4.getCodecMime(), "\"", "\"")));
					final String[] size = mp4.getSize().split("x");
					representation.add(new Attr("width", size[0]));
					representation.add(new Attr("height", size[1]));
					representation.add(new Attr("startWithSAP", "1"));
					representation.add(new Attr("maxPlayoutRate", "1"));
					representation.add(new Attr("bandwidth", String.valueOf(mp4.getBitrate())));
					representation.add(new Attr("frameRate", String.valueOf(mp4.getFPS())));
					representation.add(lXML.e("BaseURL", urlizer(mp4.videoURL())));
					if (mp4.validSegmentPayloads()) {
						final E segmentBase = representation.add(lXML.e("SegmentBase"));
						segmentBase.add(new Attr("indexRange", /* invertDJSMuxingII ? mp4.getInitPayload() : */ mp4.getIndexPayload()));
						final E initialization = segmentBase.add(lXML.e("Initialization"));
//						if (djsSingleRangeHack) {
//							final String _0 = "0";
//							final String _1 = mp4.getIndexPayload().split("-")[1];
//							initialization.add(new Attr("range", _0 + "-" + _1));
//						}else {
						initialization.add(new Attr("range", /* invertDJSMuxingII ? mp4.getIndexPayload() : */ mp4.getInitPayload()));
//						}
					}
				}
			}
			mp4video.clear();
			log.debug("[DJS] generate::mp4_video");
			return null;
		});
		ExecutorManager.get().await(mp4_audio, webm_audio, mp4_video_all);
		return d.toString();
	}
}
